package main

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"errors"
	"flag"
	"fmt"
	"io"
	"log"
	"net"
)

// FixLength 固定长度
const FixLength int = 16

const DelimiterSingle byte = 'P'

//const DelimiterMulti string = "PH"

// FieldLength 包长度字段所占字节数
const FieldLength int = 4

// goim 包相关字段
const (
	MaxBodySize    = int32(1 << 12)
	_packSize      = 4
	_headerSize    = 2
	_verSize       = 2
	_opSize        = 4
	_seqSize       = 4
	_rawHeaderSize = _packSize + _headerSize + _verSize + _opSize + _seqSize
	_maxPackSize   = MaxBodySize + int32(_rawHeaderSize)
	// offset
	_packOffset   = 0
	_headerOffset = _packOffset + _packSize
	_verOffset    = _headerOffset + _headerSize
	_opOffset     = _verOffset + _verSize
	_seqOffset    = _opOffset + _opSize
)

func main() {
	var handle int
	flag.IntVar(&handle, "handle", 0, "拆包方式")
	flag.Parse()
	fmt.Println(handle)

	handle = 1

	listen, err := net.Listen("tcp", "127.0.0.1:9000")
	if err != nil {
		log.Fatalf("listen error:%v\n", err)
	}
	for {
		conn, err := listen.Accept()
		if err != nil {
			log.Printf("accept error.%v\n", err)
			continue
		}

		// 连接自治
		switch handle {
		case 0:
			go fixLengthHandleConn(conn) //固定长度粘包
		case 1:
			go fixDelimiterHandleConn(conn) //固定分隔符粘包
		case 2:
			go lengthFieldHandleConn(conn) // 包长度字段
		case 3:
			go goimHandleConn(conn) // goim协议解码器
		}

	}
}

// fixLengthHandleConn 固定长度拆包
func fixLengthHandleConn(conn net.Conn) {
	defer conn.Close()

	buf := make([]byte, FixLength) // 新增一个缓冲区
	// 读写缓冲区
	rd := bufio.NewReader(conn)
	wr := bufio.NewWriter(conn)
	for {
		_, err := io.ReadFull(rd, buf)
		if err != nil {
			log.Printf("fixLengthHandleConn io readFull err:%v\n", err)
			continue
		}

		b := make([]byte, len(buf)) //拷贝也业务层去做处理
		copy(b, buf)
		// Todo 拿p去做相关业务做相关业务，考虑goroutine
		go func() {
			// 模拟应答-原包返回
			wr.Write(b)
			wr.Flush() // 直接syscall
		}()

	}
}

// fixDelimiterHandleConn 固定分割符拆包
// 为了演示方便，特殊分隔符为'\n'
// 且包尾进行校验和，1个字节，取最低位字节，例如累加和为0x00345678，则最低位字节为0x78。
// 默认取固定长度为FixLength
func fixDelimiterHandleConn(conn net.Conn) {
	defer conn.Close()

	// 读写缓冲区
	rd := bufio.NewReader(conn)
	//wr := bufio.NewWriter(conn)

	//buf := make([]byte, FixLength) // 新增一个缓冲区
	//var Prefix []byte
	for {
		// 第一种方法，特殊字符换行 利用rd.ReadLine()
		_, _, err := rd.ReadLine() // 依赖的是0x0a\n
		if err != nil {
			log.Printf("fixDelimiterHandleConn rd.ReadLine err:%v\n", err)
			break
		}
		// Todo 拿p去做相关业务做相关业务，考虑goroutine
		go func() {
			// 模拟应答-原包返回
			//wr.Write([]byte("1111"))
			//wr.Flush() // 直接syscall
			conn.Write([]byte("1"))
		}()

		//// 第二种方法，单字符,比如 rd.ReadSlice()
		//p, err := rd.ReadSlice(DelimiterSingle) // 依赖的是'P' 0x50
		//if err != nil {
		//	log.Printf("fixDelimiterHandleConn rd.ReadLine err:%v\n", err)
		//	break
		//}
		//// Todo 拿p去做相关业务做相关业务，考虑goroutine
		//go func() {
		//	// 模拟应答-原包返回
		//	wr.Write(p)
		//	wr.Flush() // 直接syscall
		//}()

		//// 第三种：scan \n  以及包带累加校验和
		//_, err := io.ReadFull(rd, buf)
		//if err != nil {
		//	log.Printf("fixLengthHandleConn io readFull err:%v\n", err)
		//	continue
		//}
		//
		//if Prefix != nil {
		//	buf = append(Prefix, buf...)
		//}
		//// 默认依赖\n进行拆包
		//scanner := bufio.NewScanner(
		//	strings.NewReader(string(buf)),
		//)
		//var bufT []byte
		//for scanner.Scan() {
		//	// 不为空
		//	if bufT != nil {
		//		lb, err := NumMixByte(bufT[:len(bufT)-1]) // 校验和
		//		if err != nil {
		//			log.Printf("NumMixByte err:%v\n", err)
		//			continue
		//		}
		//		if bufT[len(bufT)-1] == lb {
		//			// Todo 拿p去做相关业务做相关业务，考虑goroutine
		//			go func() {
		//				// 模拟应答-原包返回
		//				wr.Write(buf)
		//				wr.Flush() // 直接syscall
		//			}()
		//		}
		//	}
		//	bufT = scanner.Bytes() // scanner.Bytes()
		//}
		//// 最后的一个包要经过特殊处理
		//if bufT != nil {
		//	lb, err := NumMixByte(bufT[:len(bufT)-1])
		//	if err != nil {
		//		log.Printf("NumMixByte err:%v\n", err)
		//	} else {
		//		if bufT[len(bufT)-1] == lb {
		//			// Todo 拿p去做相关业务做相关业务，考虑goroutine
		//			go func() {
		//				// 模拟应答-原包返回
		//				wr.Write(buf)
		//				wr.Flush() // 直接syscall
		//			}()
		//		} else {
		//			Prefix = bufT
		//		}
		//	}
		//} else {
		//	Prefix = bufT
		//}
	}
}

// lengthFieldHandleConn 包长度字段
func lengthFieldHandleConn(conn net.Conn) {
	defer conn.Close()

	l := make([]byte, FieldLength) // 包长字段
	// 读写缓冲区
	rd := bufio.NewReader(conn)
	wr := bufio.NewWriter(conn)
	for {
		_, err := io.ReadFull(rd, l)
		if err != nil {
			log.Printf("lengthFieldHandleConn io readFull err:%v\n", err)
			continue
		}
		bodyLength, err := BytesToInt32(l, binary.BigEndian) // 这里使用大端模式
		if err != nil {
			log.Printf("lengthFieldHandleConn BytesToInt err:%v\n", err)
			continue
		}
		body := make([]byte, int(bodyLength)-FieldLength)
		_, err = io.ReadFull(rd, body)
		if err != nil {
			log.Printf("lengthFieldHandleConn io readFull err:%v\n", err)
			continue
		}
		// Todo 拿p去做相关业务做相关业务，考虑goroutine
		go func() {
			// 模拟应答-原包返回
			wr.Write(body)
			wr.Flush() // 直接syscall
		}()
	}
}

// goimHandleConn goim协议解码
func goimHandleConn(conn net.Conn) {
	defer conn.Close()

	p := make([]byte, _rawHeaderSize) // 头
	// 读写缓冲区
	rd := bufio.NewReader(conn)
	wr := bufio.NewWriter(conn)
	for {
		_, err := io.ReadFull(rd, p)
		if err != nil {
			log.Printf("goimHandleConn io readFull err:%v\n", err)
			break
		}
		pack := goimPack{}
		packSize, err := BytesToInt32(p[_packOffset:_headerOffset], binary.BigEndian)
		if err != nil {
			log.Printf("BytesToInt32 packSize err:%v\n", err)
			continue
		}
		headerSize, err := BytesToInt16(p[_headerOffset:_verOffset], binary.BigEndian)
		if err != nil {
			log.Printf("BytesToInt32 headerSize err:%v\n", err)
			continue
		}
		pack.verSize, err = BytesToInt16(p[_verOffset:_opOffset], binary.BigEndian)
		if err != nil {
			log.Printf("BytesToInt32 verSize err:%v\n", err)
			continue
		}
		pack.opSize, err = BytesToInt32(p[_opOffset:_seqOffset], binary.BigEndian)
		if err != nil {
			log.Printf("BytesToInt32 opSize err:%v\n", err)
			continue
		}
		pack.seqSize, err = BytesToInt32(p[_seqOffset:], binary.BigEndian)
		if err != nil {
			log.Printf("BytesToInt32 seqSize err:%v\n", err)
			continue
		}

		if packSize > _maxPackSize {
			break
		}
		if headerSize != _rawHeaderSize {
			break
		}
		if bodyLen := int(packSize - int32(headerSize)); bodyLen > 0 {
			pack.body = make([]byte, bodyLen)
			_, err := io.ReadFull(rd, pack.body)
			if err != nil {
				log.Printf("goimHandleConn body io readFull err:%v\n", err)
				continue
			}
			// Todo 拿p去做相关业务做相关业务，考虑goroutine
			go func() {
				// 模拟应答-返回包体
				wr.Write((&pack).body)
				wr.Flush() // 直接syscall
			}()
		} else {
			pack.body = nil
		}
	}
}

type goimPack struct {
	verSize int16
	opSize  int32
	seqSize int32
	body    []byte
}

/************************************tool********************************************/

// NumMixByte 校验和 取最小字节
func NumMixByte(data []byte) (byte, error) {
	var sum int = 0
	for _, b := range data {
		sum += int(b)
	}
	lb, err := IntToBytes(sum, binary.LittleEndian)
	if err != nil {
		return 0, err
	} else {
		if lb != nil && len(lb) > 0 {
			return lb[0], nil
		} else {
			return 0, errors.New("no valid values were obtained")
		}
	}
}

//IntToBytes 整形转换成字节
func IntToBytes(n int, order binary.ByteOrder) ([]byte, error) {
	x := int32(n)
	bytesBuffer := bytes.NewBuffer([]byte{})
	err := binary.Write(bytesBuffer, order, x)
	if err != nil {
		return nil, err
	}
	return bytesBuffer.Bytes(), nil
}

//BytesToInt32 字节转换成整形
func BytesToInt32(b []byte, order binary.ByteOrder) (int32, error) {
	bytesBuffer := bytes.NewBuffer(b)
	var x int32
	err := binary.Read(bytesBuffer, order, &x)
	if err != nil {
		return 0, err
	}
	return x, nil
}

//BytesToInt16 字节转换成整形
func BytesToInt16(b []byte, order binary.ByteOrder) (int16, error) {
	bytesBuffer := bytes.NewBuffer(b)
	var x int16
	err := binary.Read(bytesBuffer, order, &x)
	if err != nil {
		return 0, err
	}
	return x, nil
}

//func Int32(b []byte) int32 {
//	return int32(b[3]) | int32(b[2])<<8 | int32(b[1])<<16 | int32(b[0])<<24
//}
